﻿#region Copyright
// Copyright (c) 2011-2012 RaisingForce Team (Alikin Sergey)
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Diagnostics.Contracts;
using System.Xml.Serialization;
using RaisingForce.Raise.Language.Rules;

namespace RaisingForce.Raise.Language.Model
{
    [Serializable]
    [XmlType(TypeName = "Word")]
    sealed public class WordData
    {
        #region Constructors

        public WordData()
        {
            this.wordTypeName = WordTypeNames.ChainName;
            this.primaryGlyphTypeName = ChainGlyph.Name;
            this.primaryGlyphValue = SharedChainData.DefaultChainName;
            // next fields are set null by default
            // this.secondaryGlyphTypeName = null;
            // this.secondaryGlyphValue = null;
        }

        public WordData(
            string wordTypeName,
            string primaryGlyphTypeName,
            string primaryGlyphValue,
            string secondaryGlyphTypeName = null,
            string secondaryGlyphValue = null)
        {
            if (!WordTypeRules.NameIsValid(wordTypeName))
            {
                throw new ArgumentException("wordTypeName");
            }
            if (!GlyphRules.TypeNameIsValid(primaryGlyphTypeName))
            {
                throw new ArgumentException("primaryGlyphTypeName");
            }
            if (primaryGlyphValue != null
                && !GlyphRules.ValueIsValid(primaryGlyphValue))
            {
                throw new ArgumentException("primaryGlyphValue");
            }
            if (secondaryGlyphTypeName != null
                && !GlyphRules.TypeNameIsValid(secondaryGlyphTypeName))
            {
                throw new ArgumentException("secondaryGlyphTypeName");
            }
            if (secondaryGlyphValue != null
                && !GlyphRules.ValueIsValid(secondaryGlyphValue))
            {
                throw new ArgumentException("secondaryGlyphValue");
            }
            Contract.EndContractBlock();
            this.wordTypeName = wordTypeName;
            this.primaryGlyphTypeName = primaryGlyphTypeName;
            this.primaryGlyphValue = primaryGlyphValue;
            this.secondaryGlyphTypeName = secondaryGlyphTypeName;
            this.secondaryGlyphValue = secondaryGlyphValue;
        }

        #endregion

        #region Private Declarations

        private string wordTypeName;

        private string primaryGlyphTypeName;

        private string primaryGlyphValue;

        private string secondaryGlyphTypeName;

        private string secondaryGlyphValue;

        [ContractInvariantMethod]
        private void ObjectInvariant()
        {
            Contract.Invariant(WordTypeRules.NameIsValid(this.wordTypeName));
            Contract.Invariant(GlyphRules.TypeNameIsValid(this.primaryGlyphTypeName));
            Contract.Invariant(this.primaryGlyphValue == null || GlyphRules.ValueIsValid(this.primaryGlyphValue));
            Contract.Invariant(this.secondaryGlyphTypeName == null || GlyphRules.TypeNameIsValid(this.secondaryGlyphTypeName));
            Contract.Invariant(this.secondaryGlyphValue == null || GlyphRules.ValueIsValid(this.secondaryGlyphValue));
        }

        #endregion

        #region Properties

        [XmlIgnore]
        public bool HasSecondaryGlyph
        {
            get
            {
                return this.secondaryGlyphTypeName != null;
            }
        }

        [XmlAttribute(AttributeName = "Type")]
        public string WordTypeName
        {
            get
            {
                Contract.Ensures(WordTypeRules.NameIsValid(Contract.Result<string>()));
                return this.wordTypeName;
            }
            set
            {
                if (!WordTypeRules.NameIsValid(value))
                {
                    throw new ArgumentException(
                        "Invalid WordType property value.",
                        "WordType");
                }
                Contract.EndContractBlock();
                this.wordTypeName = value;
            }
        }

        [XmlAttribute(AttributeName = "PrimaryValue")]
        public string PrimaryGlyphValue
        {
            get
            {
                Contract.Ensures(Contract.Result<string>() == null || GlyphRules.ValueIsValid(Contract.Result<string>()));
                return this.primaryGlyphValue;
            }
            set
            {
                if (value != null && !GlyphRules.ValueIsValid(value))
                {
                    throw new ArgumentException(
                        "Invalid PrimaryGlyphValue property value.",
                        "PrimaryGlyphValue");
                }
                Contract.EndContractBlock();
                this.primaryGlyphValue = value;
            }
        }

        [XmlAttribute(AttributeName = "SecondaryValue")]
        public string SecondaryGlyphValue
        {
            get
            {
                Contract.Ensures(Contract.Result<string>() == null || GlyphRules.ValueIsValid(Contract.Result<string>()));
                return this.secondaryGlyphValue;
            }
            set
            {
                if (value != null && !GlyphRules.ValueIsValid(value))
                {
                    throw new ArgumentException(
                        "Invalid SecondaryGlyphValue property value.",
                        "SecondaryGlyphValue");
                }
                Contract.EndContractBlock();
                this.secondaryGlyphValue = value;
            }
        }

        #endregion

        #region Static Methods

        public static WordData CreateDefaultIfWordData()
        {
            Contract.Ensures(Contract.Result<WordData>() != null);
            return new WordData
            {
                wordTypeName = WordTypeNames.CounterIncrementing,
                primaryGlyphTypeName = CounterGlyph.Name,
                primaryGlyphValue = CounterGlyph.SingletonValue
                // next fields are set null by default
                // this.secondaryGlyphTypeName = null
                // this.secondaryGlyphValue = null
            };
        }

        public static WordData CreateDefaultThenWordData()
        {
            Contract.Ensures(Contract.Result<WordData>() != null);
            return new WordData
            {
                wordTypeName = WordTypeNames.IncrementCounter,
                primaryGlyphTypeName = CounterGlyph.Name,
                primaryGlyphValue = CounterGlyph.SingletonValue
                // next fields are set null by default
                // this.secondaryGlyphTypeName = null
                // this.secondaryGlyphValue = null
            };
        }

        public static WordData CreateDefaultChainWordData()
        {
            Contract.Ensures(Contract.Result<WordData>() != null);
            // Chain WordData is created by default .ctor
            return new WordData();
        }

        #endregion
    }
}
